// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1 ns / 1 ps

`include "espi_header.iv"

module espi_pc_put_pc_fsm
  (
   // Rx Shifter
   input logic         rx_detect_crc_end,

   // Error Detection
   input logic         pc_error_condition,

   // Posted Receive FIFO
   input logic         pc_rxfifo_empty,
   output logic        pc_rxfifo_get,
   input logic [7:0]   pc_rxfifo_rdata,
   output logic        pc_rxfifo_reset,

   // Avalon Memory-Mapped Memroy Master
   output logic        mem_req,
   input logic         mem_gnt,
   output logic [31:0] mem_address,
   output logic        mem_write,
   output logic [31:0] mem_writedata,
   output logic [3:0]  mem_byteenable,
   input logic         mem_waitrequest,

   input logic         clk,
   input logic         channel_reset_n
   );

    enum {PUT_PC_IDLE,
          PUT_PC_START,
          PUT_PC_COMMAND,
          PUT_PC_CYCLETYPE,
          PUT_PC_TAG_LENGTH_HI,
          PUT_PC_LENGTH_LO,
          PUT_PC_ADDRESS_31_24,
          PUT_PC_ADDRESS_23_16,
          PUT_PC_ADDRESS_15_8,
          PUT_PC_ADDRESS_7_0,
          PUT_PC_ACCESS_MEM,
          PUT_PC_WRITEDATA_7_0,
          PUT_PC_WRITEDATA_15_8,
          PUT_PC_WRITEDATA_23_16,
          PUT_PC_WRITEDATA_31_24,
          PUT_PC_WRITE,
          PUT_PC_LOOP,
          PUT_PC_FINISH} put_pc_state;

    logic [11:0] put_pc_length;
    logic [1:0]  byte_offset;


    // Handle PUT_PC for CYCLETYPE MEM_WRITE_32
    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            byte_offset     <= 0;
            pc_rxfifo_reset <= 0;
            pc_rxfifo_get   <= 0;
            mem_req         <= 0;
            mem_address     <= 0;
            mem_byteenable  <= 0;
            mem_write       <= 0;
            mem_writedata   <= 0;
            put_pc_length   <= 0;
        end
        else begin
            case (put_pc_state)
              PUT_PC_IDLE: begin
                  pc_rxfifo_get   <= 0;
                  pc_rxfifo_reset <= 0;
                  mem_address     <= 0;
                  mem_writedata   <= 0;
                  mem_byteenable  <= 0;
                  mem_write       <= 0;
                  put_pc_length   <= 0;
                  if (rx_detect_crc_end && !pc_error_condition) begin
                      if (!pc_rxfifo_empty) begin
                          pc_rxfifo_get <= 1'b1;
                          put_pc_state  <= PUT_PC_START;
                      end
                  end
              end // case: PUT_PC_IDLE

              PUT_PC_START: begin
                  put_pc_state <= PUT_PC_COMMAND;
              end

              PUT_PC_COMMAND: begin
                  if (pc_rxfifo_rdata != `PUT_PC) begin
                      pc_rxfifo_reset <= 1'b1;
                      put_pc_state    <= PUT_PC_FINISH;
                  end
                  else begin
                      put_pc_state <= PUT_PC_CYCLETYPE;
                  end
              end

              PUT_PC_CYCLETYPE: begin
                  if (pc_rxfifo_rdata != `MEM_WRITE_32) begin
                      pc_rxfifo_reset <= 1'b1;
                      put_pc_state    <= PUT_PC_FINISH;
                  end
                  else begin
                      put_pc_state <= PUT_PC_TAG_LENGTH_HI;
                  end
              end

              PUT_PC_TAG_LENGTH_HI: begin
                  put_pc_length[11:8] <= pc_rxfifo_rdata[3:0];
                  put_pc_state        <= PUT_PC_LENGTH_LO;
              end

              PUT_PC_LENGTH_LO: begin
                  put_pc_length[7:0] <= pc_rxfifo_rdata;
                  put_pc_state       <= PUT_PC_ADDRESS_31_24;
              end

              PUT_PC_ADDRESS_31_24: begin
                  mem_address[31:24] <= pc_rxfifo_rdata;
                  put_pc_state       <= PUT_PC_ADDRESS_23_16;
              end
                      
              PUT_PC_ADDRESS_23_16: begin
                  mem_address[23:16] <= pc_rxfifo_rdata;
                  put_pc_state       <= PUT_PC_ADDRESS_15_8;
              end
                      
              PUT_PC_ADDRESS_15_8: begin
                  mem_address[15:8] <= pc_rxfifo_rdata;
                  put_pc_state      <= PUT_PC_ADDRESS_7_0;
              end
                      
              PUT_PC_ADDRESS_7_0: begin
                  mem_address[7:0] <= pc_rxfifo_rdata & 8'hfc;
                  byte_offset      <= pc_rxfifo_rdata[1:0];
                  pc_rxfifo_get    <= 0;
                  mem_req          <= 1'b1;
                  put_pc_state     <= PUT_PC_ACCESS_MEM;
              end

              PUT_PC_ACCESS_MEM: begin
                  if (mem_gnt) begin
                      pc_rxfifo_get    <= 1'b1;
                      mem_writedata    <= 0;
                      mem_byteenable   <= 0;
                      put_pc_length    <= put_pc_length - 1'd1;
                      case (byte_offset)
                        0: begin
                            put_pc_state <= PUT_PC_WRITEDATA_7_0;
                        end

                        1: begin
                            put_pc_state <= PUT_PC_WRITEDATA_15_8;
                        end
                        
                        2: begin
                            put_pc_state <= PUT_PC_WRITEDATA_23_16;
                        end
                        
                        3: begin
                            put_pc_state <= PUT_PC_WRITEDATA_31_24;
                        end
                      endcase // case (pc_rxfifo_rdata[1:0])
                  end
              end // case: PUT_PC_ADDRESS_7_0
                      
              PUT_PC_WRITEDATA_7_0: begin
                  mem_writedata[7:0] <= pc_rxfifo_rdata;
                  mem_byteenable[0]  <= 1'b1;
                  pc_rxfifo_get      <= put_pc_length > 1;
                  if (put_pc_length > 0) begin
                      put_pc_length <= put_pc_length - 1'd1;
                      put_pc_state  <= PUT_PC_WRITEDATA_15_8;
                  end
                  else begin
                      put_pc_state <= PUT_PC_WRITE;
                  end
              end // case: PUT_PC_WRITEDATA_7_0
                      
              PUT_PC_WRITEDATA_15_8: begin
                  mem_writedata[15:8] <= pc_rxfifo_rdata;
                  mem_byteenable[1]   <= 1'b1;
                  pc_rxfifo_get       <= put_pc_length > 1;
                  if (put_pc_length > 0) begin
                      put_pc_length <= put_pc_length - 1'd1;
                      put_pc_state  <= PUT_PC_WRITEDATA_23_16;
                  end
                  else begin
                      put_pc_state <= PUT_PC_WRITE;
                  end
              end // case: PUT_PC_WRITEDATA_15_8
                      
              PUT_PC_WRITEDATA_23_16: begin
                  mem_writedata[23:16] <= pc_rxfifo_rdata;
                  mem_byteenable[2]    <= 1'b1;
                  pc_rxfifo_get        <= put_pc_length > 1;
                  if (put_pc_length > 0) begin
                      put_pc_length <= put_pc_length - 1'd1;
                      put_pc_state  <= PUT_PC_WRITEDATA_31_24;
                  end
                  else begin
                      put_pc_state <= PUT_PC_WRITE;
                  end
              end // case: PUT_PC_WRITEDATA_23_16
                      
              PUT_PC_WRITEDATA_31_24: begin
                  pc_rxfifo_get        <= 0;
                  mem_writedata[31:24] <= pc_rxfifo_rdata;
                  mem_byteenable[3]    <= 1'b1;
                  put_pc_state         <= PUT_PC_WRITE;
              end

              PUT_PC_WRITE: begin
                  mem_write    <= 1'b1;
                  put_pc_state <= PUT_PC_LOOP;
              end

              PUT_PC_LOOP: begin
                  if (!mem_waitrequest) begin
                      mem_write <= 0;
                      if (put_pc_length > 0) begin
                          mem_writedata  <= 0;
                          mem_byteenable <= 0;
                          mem_address    <= mem_address + 3'd4;
                          pc_rxfifo_get  <= put_pc_length > 1;
                          put_pc_length  <= put_pc_length - 1'd1;
                          put_pc_state   <= PUT_PC_WRITEDATA_7_0;
                      end
                      else begin
                          put_pc_state <= PUT_PC_FINISH;
                      end // else: !if(put_pc_length > 0)
                  end // if (!mem_waitrequest)
              end // case: PUT_PC_LOOP

              PUT_PC_FINISH: begin
                  mem_req      <= 0;
                  put_pc_state <= PUT_PC_IDLE;
              end
            endcase // case (put_pc_state)
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)
    
endmodule // espi_pc_put_pc_fsm
